home *** CD-ROM | disk | FTP | other *** search
/ Suzy B Software 2 / Suzy B Software CD-ROM 2 (1994).iso / adult_ed / grapher / graph / grapher.pas next >
Pascal/Delphi Source File  |  1995-05-02  |  60KB  |  1,783 lines

  1. program Grapher(input, output);
  2.  
  3. const
  4.   Pi = 3.14159265;
  5.   ClrScr = 69;       {Clear screen and home the cursor}
  6.   Cursor_on = 101;   {Turn blinking cursor on}
  7.   Cursor_off = 102;  {Turn blinking cursor off}
  8.  
  9. {$I GEMCONST.PAS}
  10.  
  11. type
  12.   Str7 = string[7];
  13.   Draw_Type = (Redraw, No_Grid, Old_Grid);
  14.   Grid_Type = (Rectangular, Polar, Trigonometric);
  15.   Res_Type = (Low, Med, Hi);
  16.   TokenType = (Numeric, Character);
  17.   NodePtr = ^Node;
  18.   Node = record
  19.     Link: NodePtr;
  20.     case NodeType: TokenType of
  21.       Numeric: (Value: real);
  22.       Character: (Code: char;
  23.                   Priority: 0..5)
  24.   end;
  25.   Screen_Type = packed array [0..31999] of byte;
  26.   Screen_Ptr = ^Screen_Type;
  27.  
  28. {$I GEMTYPE.PAS}
  29.  
  30. var
  31.   InFix: Str255;          {Infix expression}
  32.   TempPtr,                {Temporary pointer}
  33.   PostFix: NodePtr;       {Postfix expression}
  34.   X_Scale,                {Scale on X-axis}
  35.   Y_Scale: real;          {Scale used on graph grid}
  36.   Color: integer;         {Color of graph}
  37.   Draw: Draw_Type;        {Grid drawing option}
  38.   Grid: Grid_Type;        {Type of grid}
  39.   Event: integer;         {Holds Get_Event value}
  40.   DummyMsg: Message_Buffer;  {Dummy variable used in Get_Event call}
  41.   D: integer;             {Dummy variable used in Get_Event call}
  42.  
  43.   {The following variables are accessed as global variables}
  44.  
  45.   Res: Res_Type;          {Screen resolution}
  46.   X_Pix,                  {Number of pixels in horizontal direction}
  47.   Y_Pix,                  {Number of pixels in vertical direction}
  48.   X_Center,               {Center of screen horizontally}
  49.   Y_Center,               {Center of screen vertically}
  50.   Num_Color: integer;     {Number of colors available for graph}
  51.   SF: real;               {Scaling factor need to compensate for screen
  52.                            aspect ratio}
  53.   Display_Area,           {Pointer to display area in memory}
  54.   Temp_Screen: Screen_Ptr;{Used to save a graph display}
  55.   GEM_Interface: boolean; {TRUE if user selects GEM-based interface}
  56.  
  57.   GridStr,                {This and next four are used to store user}
  58.   DrawStr,                {input for grid type, grid drawing option,}
  59.   ColorStr,               {graph color,                             }
  60.   XStr,                   {X-scale, and                             }
  61.   YStr: Str255;           {Y-scale.                                 }
  62.  
  63. {$I GEMSUBS.PAS}
  64.  
  65.  
  66. {****************  Out_Escape  ******************
  67. *                                               *
  68. *  Send escape codes that control the cursor.   *
  69. *                                               *
  70. *  Called by: Initialization,                   *
  71. *             Get_Expression,                   *
  72. *             Get_Graph_Parameters_OK           *
  73. *                                               *
  74. *  In parameter: Ascii code of the character    *
  75. *                following escape code          *
  76. ************************************************}
  77.  
  78. procedure Out_Escape(c : integer);
  79.   procedure bconout(device, c:integer);
  80.     BIOS(3);
  81. begin
  82.   bconout(2, 27); {The escape character}
  83.   bconout(2, c)
  84. end; {Out_Escape}
  85.  
  86.  
  87. {*************  Initialization  *****************
  88. *                                               *
  89. *  Sets the value of various global screen      *
  90. *  parameters based on the screen resolution.   *
  91. *  Also sets initial default values of graph    *
  92. *  parameters: grid scales, color, and          *
  93. *  expression to be graphed.                    *
  94. *                                               *
  95. *  Called by: MAIN DRIVER                       *
  96. *                                               *
  97. *  All variables used in this procedure are     *
  98. *  accessed as globals                          *
  99. ************************************************}
  100.  
  101. procedure Initialization;
  102.  
  103. var
  104.   AlertStr: Str255;          {Alert box string}
  105.   Blanks: string[25];        {A string of blanks}
  106.   Num: integer;              {Dummy value used in Alert Box call}
  107.   Scr_Res: integer;          {Holds screen resolution value}
  108.   Dummy: char;
  109.  
  110.   {-------------  Physical Screen  -------------
  111.   |                                             |
  112.   |  Returns pointer to physical screen area.   |
  113.   |                                             |
  114.   |  Called by: MAIN DRIVER                     |
  115.    ---------------------------------------------}
  116.  
  117.   function Physical_Screen: Screen_Ptr;
  118.     XBIOS(2);
  119.  
  120.   {-------------  Get Resolution  --------------
  121.   |                                             |
  122.   |  Returns 0, 1, or 2 indicating current      |
  123.   |  screen resolution low, med, or high.       |
  124.   |                                             |
  125.   |  Called by: MAIN DRIVER                     |
  126.    ---------------------------------------------}
  127.  
  128.   function Get_Res : Integer;
  129.     XBIOS(4);
  130.  
  131. begin
  132.  
  133.   { Set up screen parameters based on display resolution. }
  134.  
  135.   Scr_Res := Get_Res;
  136.   case Scr_Res of
  137.  
  138.     0 : begin
  139.           Res := Low;
  140.           X_Pix := 320;
  141.           Y_Pix := 200;
  142.           SF := 0.869;
  143.           Num_Color := 15
  144.         end; {0}
  145.  
  146.     1 : begin
  147.           Res := Med;
  148.           X_Pix := 640;
  149.           Y_Pix := 200;
  150.           SF := 0.434;
  151.           Num_Color := 3
  152.         end; {1}
  153.  
  154.     2 : begin
  155.           Res := Hi;
  156.           X_Pix := 640;
  157.           Y_Pix := 400;
  158.           SF := 0.869;
  159.           Num_Color := 1
  160.         end; {3}
  161.  
  162.   end; {case}
  163.  
  164.   { Print Copyright Message }
  165.  
  166.   Out_Escape(ClrScr);
  167.   if Res = Low then
  168.     Blanks := '     '
  169.   else
  170.     Blanks := '                         ';
  171.   writeln(Blanks,'           Grapher');
  172.   writeln(Blanks,'      by Delmar Searls');
  173.   writeln;
  174.   writeln(Blanks,' (Parts of this product are');
  175.   writeln(Blanks,'Copyright (c) 1986, OSS & CCD');
  176.   writeln(Blanks,'  Used by permission of OSS)');
  177.  
  178.   { Let user select type of interface }
  179.  
  180.   AlertStr := '[2]';
  181.   AlertStr := concat(AlertStr, '[     Choose a     |');
  182.   AlertStr := concat(AlertStr,  '   text-oriented  |');
  183.   AlertStr := concat(AlertStr,  '    or GEM-based  |');
  184.   AlertStr := concat(AlertStr,  '     interface.   ]');
  185.   AlertStr := concat(AlertStr, '[ Text | GEM  ]');
  186.   Num := Do_Alert(AlertStr, 0);
  187.   if Num = 1 then
  188.     GEM_Interface := FALSE
  189.   else
  190.     GEM_Interface := TRUE;
  191.   Out_Escape(ClrScr);
  192.  
  193.   { Set clipping boundaries and find coordinates of center of display. }
  194.  
  195.   Set_Clip(0,0,X_Pix,Y_Pix);
  196.   X_Center := X_Pix DIV 2;
  197.   Y_Center := Y_Pix DIV 2;
  198.  
  199.   {  Set up initial default values  }
  200.  
  201.   GridStr := 'R';
  202.   DrawStr := '1';
  203.   ColorStr := '1'; Color := 1;
  204.   XStr := '1'; X_Scale := 1;
  205.   YStr := '1'; Y_Scale := 1;
  206.   InFix := 'SIN(X)';
  207.   Display_Area := Physical_Screen;
  208.   new(Temp_Screen)
  209. end; {Initialization)
  210.  
  211.  
  212. {****************  Str_to_Num  ******************
  213. *                                               *
  214. *  Converts a string representation of a number *
  215. *  to the numeric representation.               *
  216. *                                               *
  217. *  Called by: Next_Token, Get_Scale,            *
  218. *             Get_Graph_Parameters_OK           *
  219. *                                               *
  220. *  In parameter: The string representation      *
  221. *  Out parameter: Syntax error flag             *
  222. ************************************************}
  223.  
  224. function Str_to_Num(NumStr {in}: Str255;
  225.                     var Syntax_Error {out}: boolean): Real;
  226.  
  227. var
  228.   Integer_Part,          {Integer part of number}
  229.   Fraction_Part,         {Fraction part of number}
  230.   Power_of_Ten: real;    {Used in finding fraction part}
  231.   DP,                    {Position of decimal point}
  232.   Num_Int_Digits,        {Number of digits in integer part}
  233.   Num_Frac_Digits,       {Number of digits in fractional part}
  234.   I: integer;            {Loop counter}
  235.  
  236. begin
  237.  
  238.   { Initialize variables. }
  239.  
  240.   Integer_Part := 0;
  241.   Fraction_Part := 0;
  242.   Power_of_Ten := 1;
  243.  
  244.   { Determine number of digits in integer part and fraction part. }
  245.  
  246.   DP := pos('.', NumStr);
  247.   if DP = 0 then begin  { string represents an integer }
  248.     Num_Int_Digits := length(NumStr);
  249.     Num_Frac_Digits := 0
  250.   end {if}
  251.   else begin  { string represents a real }
  252.     Num_Int_Digits := DP-1;
  253.     Num_Frac_Digits := length(NumStr)-DP
  254.   end; {else}
  255.  
  256.   {  Convert integer part to numeric form. }
  257.  
  258.   for I := 1 to Num_Int_Digits do begin
  259.     Integer_Part := 10*Integer_Part + ord(NumStr[1]) - ord('0');
  260.     delete(NumStr,1,1)
  261.   end; {for}
  262.  
  263.   if NumStr <> '' then  { delete decimal point from string }
  264.     delete(NumStr,1,1);
  265.  
  266.   {  Convert fraction part (if any) to numeric form. }
  267.  
  268.   if Num_Frac_Digits > 0 then  { first check for extra decimal point }
  269.     if pos('.', NumStr) = 0 then begin  { conversion process }
  270.       for I := 1 to Num_Frac_Digits do begin
  271.         Fraction_Part := 10*Fraction_Part + ord(NumStr[1]) - ord('0');
  272.         Power_of_Ten := 10*Power_of_Ten;
  273.         delete(NumStr,1,1)
  274.       end; {for}
  275.       Fraction_Part := Fraction_Part/Power_of_Ten
  276.     end {if}
  277.     else
  278.       Syntax_Error := TRUE;
  279.  
  280.   Str_to_Num := Integer_Part + Fraction_Part
  281. end; {Str_to_Num}
  282.  
  283.  
  284. {**************  Convert  ***********************
  285. *                                               *
  286. *  This function converts the input expression  *
  287. *  from infix to postfix notation.  A pointer   *
  288. *  to the postfix expression is returned as the *
  289. *  value of Convert.                            *
  290. *                                               *
  291. *  Called by: Get_Expression                    *
  292. *                                               *
  293. *  In parameter: The infix expression           *
  294. *  Out parameter: Syntax error flag             *
  295. ************************************************}
  296.  
  297. function Convert(InString {in}: Str255;
  298.                  var Syntax_Error {out}: boolean): NodePtr;
  299.  
  300. var
  301.   TempStr: Str255; {Temporary storage of Infix expression}
  302.   PostFix,         {Pointer to the postfix expression}
  303.   Tail,            {Pointer to last token in postfix expression}
  304.   Token,           {A token to be added to postfix expression}
  305.   TOS: NodePtr;    {Pointer to top of stack used in conversion}
  306.   I,               {Loop counter}
  307.   L: integer;      {Length of InFix expression}
  308.   Previous_Token: char;  {Denotes the type of the previous token.  This
  309.                           has a value of '(' for right parenthesis, and
  310.                           a 'N' if previous token was numeric.  Numeric
  311.                           tokens are numbers, 'X', and ')'.  A code of
  312.                           'F' indicates a function token.  Otherwise
  313.                           this identifier is assigned the null character. }
  314.  
  315.   {------------  Next_Token  -------------------
  316.   |                                             |
  317.   |  This function removes the next item from   |
  318.   |  the infix expression and returns the       |
  319.   |  corresponding token.                       |
  320.   |                                             |
  321.   |  Called by: Convert                         |
  322.   |                                             |
  323.   |  In/Out parameter: The infix expression     |
  324.   |                    Previous token           |
  325.   |  Out parameter: Syntax error flag           |
  326.    ---------------------------------------------}
  327.  
  328.   function Next_Token(var InFix {in/out}: Str255;
  329.                       var Previous_Token: char;
  330.                       var Syntax_Error {out}: boolean): NodePtr;
  331.  
  332.   var
  333.     Token: NodePtr;   {The new token}
  334.     TStr: Str255;     {Stores numeric operand in string form}
  335.     TChar: char;      {Token code for non-numeric tokens}
  336.     T: integer;       {Temporary storage for token priority}
  337.  
  338.   begin
  339.  
  340.     { Get and initialize token node. }
  341.  
  342.     new(Token);
  343.     Token^.Link := NIL;
  344.  
  345.     while InFix[1] = ' ' do  { remove leading blanks }
  346.       delete(InFix,1,1);
  347.  
  348.     TStr := InFix[1];  { Transfer first character of infix to TStr. }
  349.     delete(InFix,1,1);
  350.  
  351.     if TStr[1] in ['0'..'9','.'] then begin  { Token is a number. }
  352.       Token^.NodeType := Numeric;
  353.  
  354.       { Read the number as a string of valid numeric characters. }
  355.  
  356.       while (InFix <> '') and (InFix[1] in ['.','0'..'9']) do begin
  357.         TStr := concat(TStr, InFix[1]);
  358.         delete(InFix,1,1)
  359.       end; {while}
  360.  
  361.       { Convert string representation to numeric. }
  362.  
  363.       Token^.Value := Str_to_Num(TStr, Syntax_Error);
  364.  
  365.       { Do a little error checking.  A number cannot directly follow
  366.         another numeric token or a function token. }
  367.  
  368.       if NOT Syntax_Error then
  369.         if (Previous_Token = 'N') OR (Previous_Token = 'F') then
  370.           Syntax_Error := TRUE
  371.         else  {reset previous token code}
  372.            Previous_Token := 'N'
  373.     end {if}
  374.     else begin  { Token is character type token. }
  375.       Token^.NodeType := Character;
  376.       TChar := TStr[1];
  377.       Token^.Code := TChar;
  378.  
  379.       { Determine priority of token }
  380.  
  381.       case TChar of
  382.         'X','(',')': Token^.Priority := 0;
  383.                 '+': Token^.Priority := 1;
  384.                 '-': if Previous_Token = '(' then begin
  385.                        Token^.Priority := 3;
  386.                        TChar := '~';
  387.                        Token^.Code := '~'
  388.                      end {if}
  389.                      else
  390.                        Token^.Priority := 1;
  391.             '*','/': Token^.Priority := 2;
  392.                 '^': Token^.Priority := 4;
  393.  
  394.                 { Also check for syntax errors in function tokens. }
  395.  
  396.                 'A': if (Length(InFix) > 1) and (InFix[1] = 'B')
  397.                                               and (InFix[2] = 'S') then begin
  398.                        Token^.Priority := 5;
  399.                        delete(InFix,1,2)
  400.                      end {if}
  401.                      else
  402.                        Syntax_Error := TRUE;
  403.                 'C': if (Length(InFix) > 1) and (InFix[1] = 'O')
  404.                                               and (InFix[2] = 'S') then begin
  405.                        Token^.Priority := 5;
  406.                        delete(InFix,1,2)
  407.                      end {if}
  408.                      else
  409.                        Syntax_Error := TRUE;
  410.                 'E': if (Length(InFix) > 1) and (InFix[1] = 'X')
  411.                                               and (InFix[2] = 'P') then begin
  412.                        Token^.Priority := 5;
  413.                        delete(InFix,1,2)
  414.                      end {if}
  415.                      else
  416.                        Syntax_Error := TRUE;
  417.                 'L': if (Length(InFix) > 0) and (InFix[1] = 'N') then begin
  418.                        Token^.Priority := 5;
  419.                        delete(InFix,1,1)
  420.                      end {if}
  421.                      else
  422.                        Syntax_Error := TRUE;
  423.                 'S': if (Length(InFix) > 1) and (InFix[1] = 'I')
  424.                                               and (InFix[2] = 'N') then begin
  425.                        Token^.Priority := 5;
  426.                        delete(InFix,1,2)
  427.                      end {if}
  428.                      else if (Length(Infix)>1) and (Infix[1] = 'Q')
  429.                                                and (Infix[2] = 'R') then begin
  430.                        Token^.Priority := 5;
  431.                        Token^.Code := 'R';
  432.                        delete(InFix,1,2)
  433.                      end {else if}
  434.                      else
  435.                        Syntax_Error := TRUE;
  436.                 'T': if (Length(Infix) > 1) and (InFix[1] = 'A')
  437.                                               and (InFix[2] = 'N') then begin
  438.                        Token^.Priority := 5;
  439.                        delete(InFix,1,2)
  440.                      end {if}
  441.                      else
  442.                        Syntax_Error := TRUE;
  443.           OTHERWISE: Syntax_Error := TRUE  { Since token was not in list }
  444.       end; {case}
  445.  
  446.       if NOT Syntax_Error then begin
  447.  
  448.         { Do a little error checking. }
  449.  
  450.         T := Token^.Priority;
  451.         if ((T = 5) OR (TChar = 'X') OR (TChar = '('))
  452.                  AND (Previous_Token = 'N') then
  453.           Syntax_Error := TRUE
  454.         else if ((T = 5) OR (TChar = 'X'))
  455.                  AND (Previous_Token = 'F') then
  456.           Syntax_Error := TRUE
  457.         else if ((T = 1) OR (T = 2) OR (T = 4) OR (TChar = ')'))
  458.                  AND (Previous_Token <> 'N') then
  459.           Syntax_Error := TRUE;
  460.  
  461.         { Reset previous token code. }
  462.  
  463.         if NOT Syntax_Error then
  464.           if Token^.Nodetype = Numeric then
  465.             Previous_Token := 'N'
  466.           else if TChar in ['X',')'] then
  467.             Previous_Token := 'N'
  468.           else if TChar = '(' then
  469.             Previous_Token := '('
  470.           else if T = 5 then
  471.             Previous_Token := 'F'
  472.           else
  473.             Previous_Token := chr(0)
  474.       end {if}
  475.     end; {else}
  476.     Next_Token := Token
  477.   end; {Next_Token}
  478.  
  479.   {------------------  Append  -----------------
  480.   |                                             |
  481.   |  This procedure appends the input token to  |
  482.   |  the postfix expression.                    |
  483.   |                                             |
  484.   |  Called by: Convert                         |
  485.   |                                             |
  486.   |  In parameter: The token                    |
  487.   |  In/Out parameter: Pointer to last token    |
  488.   |                    in postfix expression    |
  489.    ---------------------------------------------}
  490.  
  491.   procedure Append(var Tail {in/out}: NodePtr;
  492.                        Item {in}: NodePtr);
  493.  
  494.   var Temp: NodePtr;
  495.  
  496.   begin
  497.     if Item^.Link <> NIL then  {Item is on stack, append copy to postfix. }
  498.       new(Temp)
  499.     else  { The item itself is appended to postfix. }
  500.       Temp := Item;
  501.     Temp^ := Item^;
  502.     Tail^.Link := Temp;
  503.     Tail := Temp;
  504.     Temp^.Link := NIL
  505.   end; {Append}
  506.  
  507.   {-----------------  Push  --------------------
  508.   |                                             |
  509.   |  Push a token onto the stack                |
  510.   |                                             |
  511.   |  Called by: Convert                         |
  512.   |                                             |
  513.   |  In parameter: The token                    |
  514.   |  In/Out parameter: The top of stack ptr     |
  515.    ---------------------------------------------}
  516.  
  517.   procedure Push(var TOS {in/out}: NodePtr;
  518.                      Item {in}: NodePtr);
  519.  
  520.   begin
  521.     Item^.Link := TOS;
  522.     TOS := Item
  523.   end;
  524.  
  525.   {-------------------  Pop --------------------
  526.   |                                             |
  527.   |  Delete the top element from the stack.     |
  528.   |                                             |
  529.   |  Called by: Convert                         |
  530.   |                                             |
  531.   |  In/Out parameter: The top of stack ptr     |
  532.    ---------------------------------------------}
  533.  
  534.   procedure Pop(var TOS {in/out}: NodePtr);
  535.  
  536.   var
  537.     Temp: NodePtr;
  538.  
  539.   begin
  540.     Temp := TOS;
  541.     TOS := TOS^.Link;
  542.     dispose(Temp)
  543.   end; {Pop}
  544.  
  545. {********    Convert code starts here    *******}
  546.  
  547. begin
  548.   TempStr := InString;
  549.   Syntax_Error := FALSE;
  550.   Previous_Token := '(';
  551.  
  552.   { Create 'NULL' node on stack. }
  553.  
  554.   new(TOS);
  555.   TOS^.NodeType := Character;
  556.   TOS^.Priority := 0;
  557.   TOS^.Code := '@';
  558.   TOS^.Link := NIL;
  559.  
  560.   {Create a dummy head node. }
  561.  
  562.   new(PostFix);
  563.   Tail := PostFix;
  564.  
  565.   { Process the user's infix expression. }
  566.  
  567.   while (Length(InString) > 0) and not Syntax_Error do begin
  568.     Token := Next_Token(InString, Previous_Token, Syntax_Error);
  569.     if not Syntax_Error then begin
  570.  
  571.       { Numbers and variable X are immediately appended to postfix. }
  572.  
  573.       if Token^.NodeType = Numeric then
  574.         Append(Tail, Token)
  575.       else if Token^.Code = 'X' then
  576.         Append(Tail, Token)
  577.  
  578.       { Left parenthesis is pushed onto the stack. }
  579.  
  580.       else if Token^.Code = '(' then
  581.         Push(TOS, Token)
  582.  
  583.       { When a right parenthesis is encountered,  operators are pulled
  584.         from the stack and appended to postfix until the corresponding
  585.         left parenthesis is encountered.  The left parenthesis is
  586.         pulled from the stack, and both parentheses are discarded. }
  587.  
  588.       else if Token^.Code = ')' then begin
  589.         while (TOS^.Code <> '(') and (TOS^.Code <> '@') do begin
  590.           Append(Tail, TOS);
  591.           Pop(TOS)
  592.         end; {while}
  593.         if TOS^.Code = '@' then
  594.           Syntax_Error := TRUE
  595.         else
  596.           Pop(TOS)
  597.       end {else if}
  598.  
  599.       { The only thing left is operators.  Operators of higher priority,
  600.         if any, are pulled from the stack and appended to postfix.  The
  601.         current operator is then pushed onto the stack. }
  602.  
  603.       else begin
  604.         while Token^.Priority <= TOS^.Priority do begin
  605.           Append(Tail, TOS);
  606.           Pop(TOS)
  607.         end; {while}
  608.         Push(TOS, Token)
  609.       end {else}
  610.     end {if}
  611.   end; {while}
  612.   if Syntax_Error then begin  { Print syntax error message if needed. }
  613.     if GEM_Interface then begin
  614.       Out_Escape(ClrScr);
  615.       writeln('Y = ',TempStr)
  616.     end; {if}
  617.     L := length(TempStr) - length(InString) + 4;
  618.     for I := 1 to L do
  619.       write(' ');
  620.     writeln('^');
  621.     writeln('Syntax error!')
  622.   end {if}
  623.  
  624.   { Remove the remaining operators from the stack and append to postfix. }
  625.  
  626.   else begin
  627.     while TOS^.Code <> '@' do begin
  628.       if TOS^.Code = '(' then begin
  629.         Syntax_Error := TRUE;
  630.         if GEM_Interface then
  631.           Out_Escape(ClrScr);
  632.         writeln('Unmatched Left Parenthesis!')
  633.       end;
  634.       Append(Tail, TOS);
  635.       Pop(TOS)
  636.     end; {while}
  637.     Pop(TOS)  { Pull NULL node from stack }
  638.   end; {else}
  639.  
  640.   Convert := PostFix^.Link;
  641.   dispose(PostFix)
  642. end; {Convert}
  643.  
  644.  
  645. {****************  Evaluate  ********************
  646. *                                               *
  647. *  Evaluates the Postfix expression for the     *
  648. *  value of X passed to it.                     *
  649. *                                               *
  650. *  Called by: Get_Expression, Rect_Graph,       *
  651. *             and Polar_Graph                   *
  652. *                                               *
  653. *  In parameters: The postfix expression and    *
  654. *                 The value of X                *
  655. *  Out parameters: Postfix error flag and       *
  656. *                  Undefined result flag        *
  657. ************************************************}
  658.  
  659. function Evaluate(Head {in}: NodePtr;
  660.                   X {in}: real;
  661.                   var PostFix_Error {out},
  662.                       Undefined {out}: boolean): real;
  663.  
  664. var
  665.   TOS: 0..100;
  666.   Stack: array [1..100] of real;
  667.   Cosine_Val: real;
  668.   Temp: integer;
  669.  
  670. begin
  671.  
  672.   { Initialize flags and data stack. }
  673.  
  674.   PostFix_Error := FALSE;
  675.   Undefined := FALSE;
  676.   TOS := 0;
  677.  
  678.   { Process postfix expression }
  679.  
  680.   while (Head <> NIL) and not PostFix_Error and not Undefined do begin
  681.  
  682.     { Push numbers onto the stack, }
  683.  
  684.     if Head^.NodeType = Numeric then begin
  685.       TOS := TOS + 1;
  686.       Stack[TOS] := Head^.Value
  687.     end {if}
  688.  
  689.     { or push the value of the variable onto the stack, }
  690.  
  691.     else if Head^.Code = 'X' then begin
  692.       TOS := TOS + 1;
  693.       Stack[TOS] := X
  694.     end {else if}
  695.  
  696.     { or apply negation operator, }
  697.  
  698.     else if Head^.Priority = 3 then
  699.       if TOS>0 then
  700.         Stack[TOS] := -Stack[TOS]
  701.       else
  702.         PostFix_Error := TRUE
  703.  
  704.     { or apply function to TOS element, }
  705.  
  706.     else if Head^.Priority = 5 then
  707.       if TOS>0 then
  708.         case Head^.Code of
  709.           'A': Stack[TOS] := ABS(Stack[TOS]);
  710.           'C': Stack[TOS] := COS(Stack[TOS]);
  711.           'E': if Stack[TOS] < -50 then
  712.                  Stack[TOS] := 0
  713.                else if Stack[TOS] < 50 then
  714.                  Stack[TOS] := EXP(Stack[TOS])
  715.                else
  716.                  Undefined := TRUE;
  717.           'L': if Stack[TOS] > 0 then
  718.                  Stack[TOS] := LN(Stack[TOS])
  719.                else
  720.                  Undefined := TRUE;
  721.           'R': if Stack[TOS] >= 0 then
  722.                  Stack[TOS] := SQRT(Stack[TOS])
  723.                else
  724.                  Undefined := TRUE;
  725.           'S': Stack[TOS] := SIN(Stack[TOS]);
  726.           'T': begin
  727.                  Cosine_Val := COS(Stack[TOS]);
  728.                  if ABS(Cosine_Val) > 0.000001 then
  729.                    Stack[TOS] := SIN(Stack[TOS])/COS(Stack[TOS])
  730.                  else
  731.                    Undefined := TRUE
  732.                end {case option}
  733.         end {case}
  734.       else
  735.         PostFix_Error := TRUE
  736.  
  737.     { or else the token is a binary operator which is applied to top
  738.       two stack elements and the result replaces both of them. }
  739.  
  740.     else if TOS>1 then begin
  741.       TOS := TOS - 1;
  742.       case Head^.Code of
  743.         '+': Stack[TOS] := Stack[TOS] + Stack[TOS+1];
  744.         '-': Stack[TOS] := Stack[TOS] - Stack[TOS+1];
  745.         '*': Stack[TOS] := Stack[TOS] * Stack[TOS+1];
  746.         '/': if ABS(Stack[TOS+1]) > 0.000001 then
  747.                Stack[TOS] := Stack[TOS] / Stack[TOS+1]
  748.              else
  749.                Undefined := TRUE;
  750.  
  751.         { The program can handle two types of exponentiation.  If the
  752.           base (TOS) is positive, the normal process of using EXP and LN
  753.           functions is used.  If the base is negative and the exponent
  754.           is an integer, then we have to apply some algebraic trickery
  755.           first.  If the base has a value of zero, the result is set
  756.           to zero as well. }
  757.  
  758.         '^': if Stack[TOS] > 0 then
  759.                Stack[TOS] := EXP(Stack[TOS+1]*LN(Stack[TOS]))
  760.              else if Stack[TOS] < 0 then begin
  761.                Temp := round(Stack[TOS+1]);
  762.                if abs(Temp - Stack[TOS+1]) < 0.000001 then begin
  763.                  Stack[TOS] := EXP(Stack[TOS+1]*LN(-Stack[TOS]));
  764.                  if Odd(Temp) then
  765.                    Stack[TOS] := -Stack[TOS]
  766.                end {if}
  767.                else
  768.                  Undefined := TRUE
  769.              end {else if}
  770.              else
  771.                Stack[TOS] := 0
  772.       end {case}
  773.     end {if}
  774.  
  775.     { If we get this far, then postfix token is invalid.  Not likely to
  776.       happen. }
  777.  
  778.     else
  779.       PostFix_Error := TRUE;
  780.  
  781.     Head := Head^.Link  { Move to next token in postfix. }
  782.   end; {while}
  783.  
  784.   { At the end, there should be only one element remaining on the stack,
  785.     namely the final result.  Otherwise, the postfix expression is invalid.
  786.     We skip this if the function is undefined for the current value of X. }
  787.  
  788.   if not Undefined then begin
  789.     if TOS = 1 then
  790.       Evaluate := Stack[TOS]
  791.     else
  792.       PostFix_Error := TRUE;
  793.  
  794.     { Print error message if necessary. }
  795.  
  796.     if PostFix_Error then begin
  797.       if GEM_Interface then
  798.         Out_Escape(ClrScr);
  799.       writeln('Postfix error detected!');
  800.       writeln;
  801.       writeln('This is usually caused by too few');
  802.       writeln('operators.  Check for missing arithmetic');
  803.       writeln('symbols; especially multiplication "*".')
  804.     end {if}
  805.   end {if}
  806. end; {Evaluate}
  807.  
  808.  
  809. {**************  Get Expression  ****************
  810. *                                               *
  811. *  This procedure asks the user to enter the    *
  812. *  expression to be graphed.  It is entered in  *
  813. *  normal infix notation and converted to       *
  814. *  postfix.                                     *
  815. *                                               *
  816. *  Called by: MAIN DRIVER                       *
  817. *                                               *
  818. *  Out Parameter: The postfix expression        *
  819. *  In/Out parameter: The infix expression       *
  820. ************************************************}
  821.  
  822. procedure Get_Expression(var InFix {in/out}: Str255;
  823.                          var PostFix {out}: NodePtr);
  824.  
  825. var
  826.   J,               {Loop counter}
  827.   Last: integer;   {Index of last character in infix expression}
  828.   Temp: real;      {Used in checking for postfix errors}
  829.   TempStr: Str255; {Temporary storage of infix expression}
  830.   Dummy,
  831.   Syntax_Error,    {TRUE if error found during conversion to postfix}
  832.   PostFix_Error: boolean;  {TRUE if error found during evaluation}
  833.   Dialog: Dialog_Ptr;  {Pointer to dialog box}
  834.   Pushed,          {Stores way in which user exited dialog box}
  835.   Prompt,          {Points to prompt in dialog box}
  836.   User_Input,      {Points to user input item in dialog box}
  837.   Quit_Btn,        {Quit button in dialog box}
  838.   Ok_Btn: integer; {Ok button in dialog box}
  839.  
  840. begin
  841.   if NOT Gem_Interface then begin
  842.  
  843.     { Print the instructions and the default infix expression. }
  844.  
  845.     Out_Escape(ClrScr);
  846.     Out_Escape(Cursor_On);
  847.     writeln;
  848.     writeln('Enter the expression you want graphed.');
  849.     writeln('(Enter "Q" to QUIT)');
  850.     writeln;
  851.     writeln('Y = ',InFix);
  852.   end; {if}
  853.  
  854.     { Get a valid infix expression from the user. }
  855.  
  856.   repeat
  857.     if GEM_Interface then begin
  858.       if Res = Low then
  859.         Dialog := New_Dialog(4,0,0,38,5)
  860.       else
  861.         Dialog := New_Dialog(4,0,0,78,5);
  862.       Prompt := Add_DItem(Dialog,G_Text,None,1,1,2,1,0,256*Black);
  863.       Set_DText(Dialog,Prompt,'Y=',3,TE_Center);
  864.       if Res = Low then begin
  865.         User_Input := Add_DItem(Dialog,G_FText,Editable,
  866.                                          3,1,34,1,0,256*Black|128);
  867.         Set_DEdit(Dialog,User_Input,'__________________________________',
  868.                                     'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  869.                                      Infix,3,TE_Left)
  870.       end {if}
  871.       else begin
  872.         User_Input := Add_DItem(Dialog,G_FText,Editable,
  873.                                    3,1,74,1,0,256*Black|128);
  874.         Set_DEdit(Dialog,User_Input,
  875.  '__________________________________________________________________________',
  876.  'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  877.   Infix,3,TE_Left)
  878.       end; {else}
  879.       Quit_Btn := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
  880.                               1,3,6,1,0,0);
  881.       Set_DText(Dialog,Quit_Btn,'QUIT',3,TE_Center);
  882.       Ok_Btn := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
  883.                               9,3,6,1,0,0);
  884.       Set_DText(Dialog,Ok_Btn,' OK ',3,TE_Center);
  885.       Center_Dialog(Dialog);
  886.       Show_Mouse;
  887.       Pushed := Do_Dialog(Dialog,User_Input);
  888.       End_Dialog(Dialog);
  889.       Hide_Mouse;
  890.       Out_Escape(ClrScr);
  891.       Delete_Dialog(Dialog);
  892.       if Pushed = Quit_Btn then
  893.         TempStr := 'Q'
  894.       else
  895.         Get_DEdit(Dialog,User_Input,TempStr)
  896.     end {if}
  897.     else begin
  898.       writeln;
  899.       write('Y = ');
  900.       readln(TempStr)
  901.     end; {else}
  902.     Syntax_Error := FALSE;
  903.     PostFix_Error := FALSE;
  904.     if TempStr <> '' then begin
  905.                {Remove trailing blanks}
  906.       InFix := TempStr;
  907.       Last := Length(InFix);
  908.       while (Last > 0) and (InFix[Last] = ' ') do begin
  909.         delete(InFix,Last,1);
  910.         Last := Last - 1
  911.       end; {while}
  912.                 {Convert to all uppercase}
  913.       for J := 1 to Last do
  914.         if InFix[J] in ['a'..'z'] then
  915.           InFix[J] := chr(ord(InFix[J])-32)
  916.     end; {if}
  917.     If InFix <> 'Q' then begin { Convert infix to postfix. }
  918.       PostFix := Convert(InFix, Syntax_Error);
  919.       if NOT Syntax_Error then { Check for postfix error. }
  920.         Temp := Evaluate(PostFix, 1.1, PostFix_Error, Dummy)
  921.     end {if}
  922.   until NOT(Syntax_Error or PostFix_Error);
  923.   if NOT GEM_Interface then
  924.     Out_Escape(Cursor_Off)
  925. end; {Get}
  926.  
  927.  
  928. {**********  Get Graph Parameters OK  ***********
  929. *                                               *
  930. *  Get the grid scale, the color of the graph,  *
  931. *  and the desired grid drawing option.         *
  932. *                                               *
  933. *  Called by: MAIN DRIVER                       *
  934. *                                               *
  935. *  Out parameters: Grid type, X & Y scales,     *
  936. *           color, and grid drawing option.     *
  937. *                                               *
  938. *  Global variables accessed: Res, Num_Color    *
  939. ************************************************}
  940.  
  941. function Get_Graph_Parameters_OK(var Grid {out}: Grid_Type;
  942.                                   var X_Scale {out}: real;
  943.                                   var Y_Scale {out}: real;
  944.                                   var Color {out}: integer;
  945.                                   var Draw {out}: Draw_Type): boolean;
  946.  
  947. var
  948.   I: integer;             {Loop counter}
  949.   TempStr: Str255;        {Temporary string representation}
  950.   Temp: real;             {Temporary numeric representation}
  951.   Syntax_Error: boolean;  {True if entry is invalid}
  952.   Dialog: Dialog_Ptr;     {Pointer to dialog box}
  953.   Button: array [1..15] of integer;  {Dialog box buttons}
  954.   Pushed: integer;        {Indicates which button was selected}
  955.   Prompt: integer;        {Pointer to prompt in dialog box}
  956.  
  957.   {--------------  Select Color  ---------------
  958.   |                                             |
  959.   |  Select color for graph line.               |
  960.   |                                             |
  961.   |  Called by: Get_Graph_Parameters_OK         |
  962.   |                                             |
  963.    ---------------------------------------------}
  964.  
  965.   procedure Select_Color;
  966.     var
  967.       I : integer;        {Loop counter}
  968.  
  969.     begin
  970.       if GEM_Interface then begin
  971.         Dialog := New_Dialog(16,0,0,8,18);
  972.         Prompt := Add_DItem(Dialog,G_Text,None,1,1,6,1,0,256*Black);
  973.         Set_DText(Dialog,Prompt,'Color?',3,TE_Center);
  974.         for I := 1 to Num_Color do
  975.           Button[I] := Add_DItem(Dialog,G_Box,Selectable|Exit_Btn,
  976.                                    2,I+1,4,1,0,112+I);
  977.         Center_Dialog(Dialog);
  978.         Show_Mouse;
  979.         Pushed := Do_Dialog(Dialog,0);
  980.         End_Dialog(Dialog);
  981.         Hide_Mouse;
  982.         Out_Escape(ClrScr);
  983.         Delete_Dialog(Dialog);
  984.         for I := 1 to Num_Color do
  985.           if Pushed = Button[I] then
  986.             Color := I
  987.       end {if}
  988.       else begin
  989.         Out_Escape(ClrScr);
  990.         for I := 1 to Num_Color do begin
  991.           Line_Color(I);
  992.           writeln(I:2,' ');
  993.           line(24, 8*I-4, 300, 8*I-4)
  994.         end; {for}
  995.         writeln;
  996.         repeat
  997.           Syntax_Error := FALSE;
  998.           write('Graph color <',ColorStr,'>: '); readln(TempStr);
  999.           if TempStr <> '' then begin
  1000.             for I := 1 to length(TempStr) do
  1001.               if not (TempStr[I] in ['0'..'9']) then
  1002.                 Syntax_Error := TRUE;
  1003.             if not Syntax_Error then begin
  1004.               Temp := Str_to_Num(TempStr, Syntax_Error);
  1005.               if (Temp < 1) or (Temp > Num_Color) then
  1006.                 Syntax_Error := TRUE
  1007.               else begin
  1008.                 ColorStr := TempStr;
  1009.                 Color := round(Temp)
  1010.               end {else}
  1011.             end {if}
  1012.           end {if}
  1013.         until not Syntax_Error
  1014.       end {else}
  1015.     end; {Select_Color}
  1016.  
  1017.   {----------------  Get Scale  ----------------
  1018.   |                                             |
  1019.   |  Get the scale for the X or Y axis.         |
  1020.   |                                             |
  1021.   |  Called by: Get_Graph_Parameters_OK         |
  1022.   |                                             |
  1023.   |  In parameter: Which axis ('X' or 'Y')      |
  1024.   |  Out parameters: The scale in both numeric  |
  1025.   |                  and string representations |
  1026.   |                                             |
  1027.   |  TempStr, Temp, and Syntax_Error are used   |
  1028.   |  as globals from calling procedure.         |
  1029.    ---------------------------------------------}
  1030.  
  1031.   procedure Get_Scale(Axis {in}: char;
  1032.                       var XYStr {out}: Str255;
  1033.                       var XY_Scale {out}: real);
  1034.  
  1035.   var
  1036.     I: integer;            {Loop counter}
  1037.  
  1038.   begin
  1039.     if GEM_Interface then begin
  1040.       Dialog := New_Dialog(9,0,0,11,20);
  1041.       Prompt := Add_DItem(Dialog,G_Text,None,1,1,9,1,0,256*Black);
  1042.       if Axis = 'X' then
  1043.        Set_DText(Dialog,Prompt,'X-Scale',3,TE_Center)
  1044.       else if Axis = 'Y' then
  1045.        Set_DText(Dialog,Prompt,'Y-Scale',3,TE_Center)
  1046.       else
  1047.        Set_DText(Dialog,Prompt,'R-Scale',3,TE_Center);
  1048.       for I := 1 to 8 do
  1049.         Button[I] := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
  1050.                                   3,2*I+2,5,1,0,0);
  1051.       Set_DText(Dialog, Button[1], '0.5',3,TE_Center);
  1052.       Set_DText(Dialog, Button[2], '1',3,TE_Center);
  1053.       Set_DText(Dialog, Button[3], '2',3,TE_Center);
  1054.       Set_DText(Dialog, Button[4], '5',3,TE_Center);
  1055.       Set_DText(Dialog, Button[5], '10',3,TE_Center);
  1056.       Set_DText(Dialog, Button[6], '20',3,TE_Center);
  1057.       Set_DText(Dialog, Button[7], '50',3,TE_Center);
  1058.       Set_DText(Dialog, Button[8], '100',3,TE_Center);
  1059.       Center_Dialog(Dialog);
  1060.       Show_Mouse;
  1061.       Pushed := Do_Dialog(Dialog,0);
  1062.       End_Dialog(Dialog);
  1063.       Hide_Mouse;
  1064.       Out_Escape(ClrScr);
  1065.       Delete_Dialog(Dialog);
  1066.       if Pushed = Button[1] then XY_Scale := 0.5
  1067.       else if Pushed = Button[2] then XY_Scale := 1.0
  1068.       else if Pushed = Button[3] then XY_Scale := 2.0
  1069.       else if Pushed = Button[4] then XY_Scale := 5.0
  1070.       else if Pushed = Button[5] then XY_Scale := 10.0
  1071.       else if Pushed = Button[6] then XY_Scale := 20.0
  1072.       else if Pushed = Button[7] then XY_Scale := 50.0
  1073.       else XY_Scale := 100.0
  1074.     end {if}
  1075.     else begin
  1076.       repeat
  1077.         Syntax_Error := FALSE;
  1078.         writeln;
  1079.         write(Axis,'-Scale <',XYStr,'>: ');readln(TempStr);
  1080.         if TempStr <> '' then begin
  1081.           for I:= 1 to length(TempStr) do
  1082.             if not (TempStr[I] in ['.','0'..'9']) then
  1083.               Syntax_Error := TRUE;
  1084.           if not Syntax_Error then
  1085.             Temp := Str_to_Num(TempStr, Syntax_Error);
  1086.           if not Syntax_Error then
  1087.             if Temp > 100 then begin
  1088.               writeln('Enter a value <= 100');
  1089.               Syntax_Error := TRUE
  1090.             end {if}
  1091.             else begin
  1092.               XYStr := TempStr;
  1093.               XY_Scale := Temp
  1094.             end {else}
  1095.         end {if}
  1096.       until not Syntax_Error
  1097.     end {else}
  1098.   end; {Get_Scale}
  1099.  
  1100. {----  Get Graph parameters starts here  ----}
  1101.  
  1102. begin
  1103.  
  1104.   {  Get Grid-type or QUIT  }
  1105.  
  1106.   if GEM_Interface then begin
  1107.     Dialog := New_Dialog(5,0,0,14,13);
  1108.     Prompt := Add_DItem(Dialog,G_Text,None,1,1,12,1,0,256*Black);
  1109.     Set_DText(Dialog,Prompt,'Grid?',3,TE_Center);
  1110.     for I := 1 to 4 do
  1111.       Button[I] := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
  1112.                                4,2*I+1,6,1,0,0);
  1113.     Set_DText(Dialog,Button[1],'RECT',3,TE_Center);
  1114.     Set_DText(Dialog,Button[2],'TRIG',3,TE_Center);
  1115.     Set_DText(Dialog,Button[3],'POLAR',3,TE_Center);
  1116.     Set_DText(Dialog,Button[4],'QUIT',3,TE_Center);
  1117.     Center_Dialog(Dialog);
  1118.     Show_Mouse;
  1119.     Pushed := Do_Dialog(Dialog,0);
  1120.     End_Dialog(Dialog);
  1121.     Hide_Mouse;
  1122.     Out_Escape(ClrScr);
  1123.     Delete_Dialog(Dialog);
  1124.     if Pushed = Button[1] then TempStr := 'R'
  1125.     else if Pushed = Button[2] then TempStr := 'T'
  1126.     else if Pushed = Button[3] then TempStr := 'P'
  1127.     else TempStr := 'Q'
  1128.   end {if}
  1129.   else begin
  1130.     Out_Escape(Cursor_on);
  1131.     Out_Escape(ClrScr);
  1132.     writeln('Enter');
  1133.     writeln;
  1134.     writeln('  "R" for rectangular grid');
  1135.     writeln;
  1136.     writeln('  "P" for polar grid');
  1137.     writeln;
  1138.     writeln('  "T" for trigonometric grid');
  1139.     writeln;
  1140.     writeln('  "Q" for QUIT (or get new function)');
  1141.     writeln;
  1142.     writeln;
  1143.     repeat
  1144.       write('Grid type <',GridStr,'>: '); readln(TempStr);
  1145.       if TempStr[1] in ['p','q','r','t'] then
  1146.         TempStr[1] := chr(ord(TempStr[1])-32)
  1147.     until (TempStr[1] in ['P', 'Q', 'R', 'T']) or (TempStr = '')
  1148.   end; {else}
  1149.   if TempStr = 'Q' then
  1150.     Get_Graph_Parameters_OK := FALSE
  1151.   else begin
  1152.     if TempStr <> '' then
  1153.       GridStr := TempStr;
  1154.     Get_Graph_Parameters_OK := TRUE;
  1155.     if GridStr = 'R' then
  1156.       Grid := Rectangular
  1157.     else if GridStr = 'P' then
  1158.       Grid := Polar
  1159.     else
  1160.       Grid := Trigonometric;
  1161.  
  1162.     {  Get grid scales  }
  1163.  
  1164.     if GEM_Interface then begin
  1165.       if Grid = Rectangular then begin
  1166.         Get_Scale('X', XStr, X_Scale);
  1167.         Get_Scale('Y', YStr, Y_Scale)
  1168.       end {if}
  1169.       else if Grid = Polar then
  1170.         Get_Scale('R', XStr, X_Scale)
  1171.       else begin
  1172.         X_Scale := Pi/2;
  1173.         Get_Scale('Y', YStr, Y_Scale)
  1174.       end {else}
  1175.     end {if}
  1176.     else begin
  1177.       Out_Escape(ClrScr);
  1178.       writeln('The origin is centered in the display');
  1179.       write('area.  You can adjust the ');
  1180.       if Grid = Rectangular then begin
  1181.         writeln('horizontal');
  1182.         writeln('and vertical scales by entering the');
  1183.         writeln('value corresponding to the first grid');
  1184.         writeln('line.  Integer values are recommended.');
  1185.         writeln;
  1186.         Get_Scale('X', XStr, X_Scale);
  1187.         Get_Scale('Y', YStr, Y_Scale)
  1188.       end {if}
  1189.       else if Grid = Polar then begin
  1190.         writeln('scale by');
  1191.         writeln('entering the value of the radius of');
  1192.         writeln('the first circle in the polar grid.');
  1193.         writeln('Integer values are recommended.');
  1194.         writeln;
  1195.         Get_Scale('R', XStr, X_Scale)
  1196.       end {else if}
  1197.       else begin
  1198.         writeln('vertical');
  1199.         writeln('scale by entering the value of the');
  1200.         writeln('first horizontal grid line.  Integer');
  1201.         writeln('values are recommended.');
  1202.         writeln;
  1203.         X_Scale := Pi/2;
  1204.         XStr := '1.57079633';
  1205.         Get_Scale('Y', YStr, Y_Scale)
  1206.       end {else}
  1207.     end; {else}
  1208.  
  1209.     {  Get Graph Color  }
  1210.  
  1211.     if Res = Hi then
  1212.       Color := Black
  1213.     else
  1214.       Select_Color;
  1215.  
  1216.     {  Get Grid drawing option  }
  1217.  
  1218.     if GEM_Interface then begin
  1219.       Dialog := New_Dialog(3,0,0,20,7);
  1220.       for I := 1 to 3 do
  1221.         Button[I] := Add_DItem(Dialog,G_Button,Selectable|Exit_Btn,
  1222.                                 1,2*I-1,18,1,0,0);
  1223.       Set_DText(Dialog,Button[1],'Clear / New Grid',3,TE_Center);
  1224.       Set_DText(Dialog,Button[2],'Clear / No Grid',3,TE_Center);
  1225.       Set_DText(Dialog,Button[3],'Draw on old Grid',3,TE_Center);
  1226.       Center_Dialog(Dialog);
  1227.       Show_Mouse;
  1228.       Pushed := Do_Dialog(Dialog,0);
  1229.       End_Dialog(Dialog);
  1230.       Hide_Mouse;
  1231.       Out_Escape(ClrScr);
  1232.       Delete_Dialog(Dialog);
  1233.       if Pushed = Button[1] then
  1234.         Draw := Redraw
  1235.       else if Pushed = Button[2] then
  1236.         Draw := No_Grid
  1237.       else
  1238.         Draw := Old_Grid
  1239.     end {if}
  1240.     else begin
  1241.       Out_Escape(ClrScr);
  1242.       writeln('Choose one of the following:');
  1243.       writeln('  1. Clear screen / Draw new grid');
  1244.       writeln('  2. Clear screen / NO GRID');
  1245.       writeln('  3. Draw graph on previous screen');
  1246.       writeln;
  1247.       repeat
  1248.         Syntax_Error := FALSE;
  1249.         write('Which option <',DrawStr,'>: '); readln(TempStr);
  1250.       until (TempStr[1] in ['1','2','3']) or (TempStr = '');
  1251.       if TempStr <> '' then begin
  1252.         DrawStr := TempStr;
  1253.         if DrawStr = '1' then
  1254.           Draw := Redraw
  1255.         else if DrawStr = '2' then
  1256.           Draw := No_Grid
  1257.         else
  1258.           Draw := Old_Grid
  1259.       end; {if}
  1260.       Out_Escape(Cursor_Off)
  1261.     end {else}
  1262.   end {else}
  1263. end; {Get_Graph_Parameters_OK}
  1264.  
  1265.  
  1266. {*************  Number to String  ***************
  1267. *                                               *
  1268. *  Converts the number parameter to string      *
  1269. *  representation.                              *
  1270. *                                               *
  1271. *  Called by: Rect_Grid, Polar_Grid             *
  1272. *                                               *
  1273. *  In parameter: The number                     *
  1274. *  Out parameter: The corresponding string      *
  1275. ************************************************}
  1276.  
  1277. procedure Num_to_Str(N {in}: real;
  1278.                      var NumStr {out}: Str7);
  1279.  
  1280. var
  1281.   Integer_Part,            {Integer part of number}
  1282.   Fraction_Part: integer;  {Fractional part of number}
  1283.   TempI,                   {Temporary integer string}
  1284.   TempF: Str7;             {Temporary fraction part string}
  1285.  
  1286. begin
  1287.  
  1288.   { Find integer and fraction parts. }
  1289.  
  1290.   if Abs(round(N) - N) < 0.01 then begin
  1291.     Integer_Part := round(N);
  1292.     Fraction_Part := 0
  1293.   end {if}
  1294.   else begin
  1295.     Integer_Part := trunc(N);
  1296.     Fraction_Part := round((N-Integer_Part)*100)
  1297.   end; {else}
  1298.  
  1299.   {  Convert integer part to string representation. }
  1300.  
  1301.   if Integer_Part = 0 then
  1302.     TempI := '0'
  1303.   else
  1304.     TempI := '';
  1305.   while Integer_Part <> 0 do begin
  1306.     TempI := concat(chr(ord('0') + Integer_Part MOD 10), TempI);
  1307.     Integer_Part := Integer_Part DIV 10
  1308.   end; {while}
  1309.  
  1310.   {  Convert fraction part to string representation. }
  1311.  
  1312.   TempF := '';
  1313.   if Fraction_Part <> 0 then begin
  1314.     while Fraction_Part <> 0 do begin
  1315.       TempF := concat(chr(ord('0') + Fraction_Part MOD 10), TempF);
  1316.       Fraction_Part := Fraction_Part DIV 10
  1317.     end; {while}
  1318.     TempF := concat('.', TempF)
  1319.   end; {if}
  1320.  
  1321.   NumStr := concat(TempI, TempF)  { Concatenate integer and fraction parts. }
  1322. end; {Num_to_Str}
  1323.  
  1324.  
  1325. {*************  Restore Screen  *****************
  1326. *                                               *
  1327. *  Restore saved screen to display area.        *
  1328. *                                               *
  1329. *  Called by: Rect_Graph, Polar_Graph           *
  1330. *                                               *
  1331. *  In parameters: Pointer to physical screen,   *
  1332. *                 Screen storage area           *
  1333. ************************************************}
  1334.  
  1335. procedure Restore_Screen(Display {in},
  1336.                          Old_Screen {in}: Screen_Ptr);
  1337.  
  1338. var
  1339.   I: integer;      {Loop control}
  1340.  
  1341. begin
  1342.   {$P-}
  1343.   for I := 0 to 31999 do
  1344.     Display^[I] := Old_Screen^[I]
  1345.   {$P+}
  1346. end; {Restore_Screen}
  1347.  
  1348.  
  1349. {***********  Draw Rectangular Graph  ***********
  1350. *                                               *
  1351. *  Draws a graph in a rectangular coordinate    *
  1352. *  system.                                      *
  1353. *                                               *
  1354. *  Called by: MAIN DRIVER                       *
  1355. *                                               *
  1356. *  In Parameters: Function being graphed,       *
  1357. *                 Grid scales, Color of graph,  *
  1358. *                 Grid drawing option           *
  1359. *                                               *
  1360. *  Globals accessed: X_Center, Y_Center, X_Pix, *
  1361. *                    SF, Display_Area and       *
  1362. *                    Temp_Screen                *
  1363. ************************************************}
  1364.  
  1365. procedure Rect_Graph(The_Function {in}: NodePtr;
  1366.                      X_Scale, Y_Scale {in}: real;
  1367.                      Graph_Color {in}: integer;
  1368.                      Draw {in}: Draw_Type);
  1369.  
  1370. var
  1371.   SX: integer;         {Loop counter and screen X-coordinate}
  1372.   SY,                  {Screen Y-coordinate}
  1373.   X, Y,                {Logical X, Y coordinates}
  1374.   XPix_per_Unit,       {Pixels per horizontal grid unit}
  1375.   YPix_per_Unit: real; {Pixels per vertical grid unit}
  1376.   Dummy,
  1377.   Undefined,           {TRUE if function is undefined for given X}
  1378.   Line_to_Flag: boolean;  {True if last logical point was plotted on screen}
  1379.  
  1380.  
  1381.   {-----------  Draw Rectangular Grid  -----------
  1382.   |                                               |
  1383.   |  Draws and labels a rectangular grid.         |
  1384.   |                                               |
  1385.   |  Called by: Rect_Graph                        |
  1386.   |                                               |
  1387.   |  In parameter: Grid scales, grid type         |
  1388.   |                                               |
  1389.   |  Global parameters accessed: Res, X_Pix,      |
  1390.   |       Y_Pix, X_Center, Y_Center, SF           |
  1391.    -----------------------------------------------}
  1392.  
  1393.   procedure Rect_Grid(X_Scale, Y_Scale {in}: real;
  1394.                          Grid_Option: Grid_Type);
  1395.  
  1396.   const
  1397.     Pi_code = 227;       {Code for Pi character}
  1398.  
  1399.   var
  1400.     X, Y: integer;       {Loop control}
  1401.     SX1, SX2, SY1, SY2,  {Screen coordinates}
  1402.     Tick_Unit,           {X-coordinate of first grid line to right of origin}
  1403.     Pix_per_Unit: real;  {Number of pixels in one grid unit}
  1404.     NumStr: Str7;        {String form of grid labels}
  1405.  
  1406.   begin
  1407.     Clear_Screen;
  1408.     if Res = Hi then
  1409.       Line_Color(Black)
  1410.     else
  1411.       Line_Color(Red);
  1412.     Pix_per_Unit := X_Center/5;
  1413.  
  1414.     { Vertical grid lines }
  1415.  
  1416.     SY1 := 0;
  1417.     SY2 := Y_Pix - 1;
  1418.     for X := -5 to 4 do begin
  1419.       SX1 := X_Center + X*Pix_per_Unit;
  1420.       Line(round(SX1),round(SY1),round(SX1),round(SY2))
  1421.     end; {for}
  1422.     SX1 := X_Pix - 1;
  1423.     Line(round(SX1),round(SY1),round(SX1),round(SY2));
  1424.  
  1425.     { Horizontal grid lines }
  1426.  
  1427.     SX1 := 0;
  1428.     SX2 := X_Pix - 1;
  1429.     for Y := -3 to 3 do begin
  1430.       SY1 := Y_Center - Y*SF*Pix_per_Unit;
  1431.       Line(round(SX1),round(SY1),round(SX2),round(SY1))
  1432.     end; {for}
  1433.     Line_Color(Black);
  1434.     Line(X_Center,0,X_Center,Y_Pix-1);
  1435.     Line(0,Y_Center,X_Pix-1,Y_Center);
  1436.  
  1437.     { X-axis labels }
  1438.  
  1439.     Draw_Mode(2);
  1440.     Tick_Unit := X_Scale;
  1441.     SY1 := Y_Center + 0.3*SF*Pix_per_Unit;
  1442.     if Grid = Rectangular then
  1443.       for X := 1 to 4 do begin
  1444.         Num_to_Str(X*Tick_Unit, NumStr);
  1445.         SX1 := X_Center + X*Pix_per_Unit - 8*Length(NumStr)/2;
  1446.         Draw_String(round(SX1),round(SY1),NumStr);
  1447.         SX1 := X_Center - X*Pix_per_Unit - 8*(Length(NumStr)+1)/2;
  1448.         NumStr := concat('-',NumStr);
  1449.         Draw_String(round(SX1),round(SY1),NumStr)
  1450.       end {for}
  1451.     else begin  {  Trigonometric X-axis labels  }
  1452.       NumStr:= chr(Pi_code);
  1453.       SX1 := X_Center + 2*Pix_per_Unit - 4;
  1454.       Draw_String(round(SX1),round(SY1),NumStr);
  1455.       SX1 := X_Center - 2*Pix_per_Unit - 8;
  1456.       NumStr := concat('-', NumStr);
  1457.       Draw_String(round(SX1),round(SY1), NumStr);
  1458.       delete(NumStr,1,1);
  1459.       NumStr := concat('2',NumStr);
  1460.       SX1 := X_Center + 4*Pix_per_Unit - 8;
  1461.       Draw_String(round(SX1),round(SY1),NumStr);
  1462.       SX1 := X_Center - 4*Pix_per_Unit - 16;
  1463.       NumStr := concat('-', NumStr);
  1464.       Draw_String(round(SX1),round(SY1), NumStr);
  1465.       delete(NumStr,1,2);
  1466.       NumStr := concat(NumStr, '/2');
  1467.       SX1 := X_Center + Pix_per_Unit - 12;
  1468.       Draw_String(round(SX1),round(SY1),NumStr);
  1469.       SX1 := X_Center - Pix_per_Unit - 16;
  1470.       NumStr := concat('-', NumStr);
  1471.       Draw_String(round(SX1),round(SY1), NumStr);
  1472.       delete(NumStr,1,1);
  1473.       NumStr := concat('3',NumStr);
  1474.       SX1 := X_Center + 3*Pix_per_Unit - 16;
  1475.       Draw_String(round(SX1),round(SY1),NumStr);
  1476.       SX1 := X_Center - 3*Pix_per_Unit - 20;
  1477.       NumStr := concat('-', NumStr);
  1478.       Draw_String(round(SX1),round(SY1), NumStr)
  1479.     end; {else}
  1480.  
  1481.     { Y-Axis labels }
  1482.  
  1483.     Tick_Unit := Y_Scale;
  1484.     SX1 := X_Center + 8;
  1485.     for Y := 1 to 3 do begin
  1486.       Num_to_Str(Y*Tick_Unit, NumStr);
  1487.       SY1 := Y_Center - Y*SF*Pix_per_Unit + 4*(Y_Pix DIV 200);
  1488.       Draw_String(round(SX1),round(SY1),NumStr);
  1489.       SY1 := Y_Center + Y*SF*Pix_per_Unit + 4*(Y_Pix DIV 200);
  1490.       NumStr := concat('-',NumStr);
  1491.       Draw_String(round(SX1),round(SY1),NumStr)
  1492.     end; {for}
  1493.     Draw_Mode(1)
  1494.   end; {Draw_RGrid}
  1495.  
  1496. {-----  Rect_Graph begins here  -----}
  1497.  
  1498. begin
  1499.   if Draw = ReDraw then
  1500.     Rect_Grid(X_Scale, Y_Scale, Grid)
  1501.   else if Draw = Old_Grid then
  1502.     Restore_Screen(Display_Area, Temp_Screen)
  1503.   else
  1504.     Clear_Screen;
  1505.   Line_Color(Graph_Color);
  1506.   XPix_per_Unit := (X_Center/5)/X_Scale;
  1507.   YPix_per_Unit := SF*(X_Center/5)/Y_Scale;
  1508.   Line_to_Flag := FALSE;
  1509.   for SX := 0 to X_Pix - 1 do begin
  1510.     X := (SX - X_Center)/XPix_per_Unit;
  1511.     Y := Evaluate(The_Function, X, Dummy, Undefined);
  1512.     if NOT Undefined then begin
  1513.       SY := Y_Center - Y*YPix_per_Unit;
  1514.       if Abs(SY) < 32000 then  { it's safe to use round function }
  1515.         if Line_to_Flag then
  1516.           Line_to(SX, round(SY))
  1517.         else begin
  1518.           Plot(SX, round(SY));
  1519.           Line_to_Flag := TRUE
  1520.         end {else}
  1521.       else
  1522.         Line_to_Flag := FALSE
  1523.     end {if}
  1524.     else
  1525.       Line_to_Flag := FALSE
  1526.   end {for}
  1527. end; {Draw_RGraph}
  1528.  
  1529.  
  1530. {*************  Draw Polar Graph  ***************
  1531. *                                               *
  1532. *  Draws a graph using a polar coordinate       *
  1533. *  system.                                      *
  1534. *                                               *
  1535. *  Called by: MAIN DRIVER                       *
  1536. *                                               *
  1537. *  In parameters: Function being graphed,       *
  1538. *                 Grid scale, Color of graph,   *
  1539. *                 Grid drawing option           *
  1540. *                                               *
  1541. *  Globals accessed: X_Center, Y_Center, X_Pix, *
  1542. *                    SF, Display_Area and       *
  1543. *                    Temp_Screen                *
  1544. ************************************************}
  1545.  
  1546. procedure Polar_Graph(The_Function {in}: NodePtr;
  1547.                       X_Scale {in}: real;
  1548.                       Graph_Color {in}: integer;
  1549.                       Draw {in}: Draw_Type);
  1550.  
  1551. var
  1552.   SX, SY,                {Screen coordinates}
  1553.   Angle,                 {Angle in radians}
  1554.   Radius,                {Radius for given angle}
  1555.   XPix_per_Unit,         {Pixels per horizontal grid unit}
  1556.   YPix_per_Unit: real;   {Pixels per vertical grid unit}
  1557.   A: integer;            {Loop counter}
  1558.   Dummy_flag,
  1559.   Undefined,             {TRUE if function is undefined for given angle}
  1560.   Line_to_Flag: boolean; {TRUE if last logical point was plotted}
  1561.   Dummy:char;
  1562.  
  1563.  
  1564.   {--------------  Draw Polar Grid  --------------
  1565.   |                                               |
  1566.   |  Draws and labels a Polar coordinate grid.    |
  1567.   |                                               |
  1568.   |  Called by: Polar_Graph                       |
  1569.   |                                               |
  1570.   |  In_Parameter: Grid scale                     |
  1571.   |                                               |
  1572.   |  Global variables accessed: Res, X_Pix,       |
  1573.   |       Y_Pix, X_Center, Y_Center, SF           |
  1574.    -----------------------------------------------}
  1575.  
  1576.   procedure Polar_Grid(X_Scale {in}: real);
  1577.  
  1578.   var
  1579.     Pix_per_Unit: real;
  1580.     R: integer;
  1581.     A: integer;
  1582.     Rad: real;
  1583.     SX1, SY1, SX2, SY2: real;
  1584.     Temp: real;
  1585.     X,Y: real;
  1586.     NumStr: Str7;
  1587.  
  1588.   begin
  1589.     Clear_Screen;
  1590.     if Res = Hi then
  1591.       Line_Color(Black)
  1592.     else
  1593.       Line_Color(Red);
  1594.  
  1595.     { Draw the concentric circles. }
  1596.  
  1597.     Pix_per_Unit := X_Center/5;
  1598.     for R := 1 to 4 do begin
  1599.       Plot(X_Center+round(Pix_per_Unit*R), Y_Center);
  1600.       for A := 1 to 72 do begin
  1601.         Rad := A*Pi/36;
  1602.         Temp := R*Cos(Rad)*Pix_per_Unit;
  1603.         SX1 := X_Center + Temp;
  1604.         Temp := R*Sin(Rad)*SF*Pix_per_Unit;
  1605.         SY1 := Y_Center - Temp;
  1606.         Line_to(round(SX1), round(SY1))
  1607.       end {for}
  1608.     end; {for}
  1609.  
  1610.     { Draw radiating lines.  Lines at 0, 30, 60,... degrees go through the
  1611.       origin.  The rest start at the second circle.  Otherwise the middle
  1612.       of the grid gets too cluttered. }
  1613.  
  1614.     for A := 0 to 35 do begin
  1615.       Rad := A*Pi/36;
  1616.       X := 4*Cos(Rad)*Pix_per_Unit;
  1617.       Y := 4*Sin(Rad)*SF*Pix_per_Unit;
  1618.       if A MOD 3 = 0 then begin
  1619.         SX1 := X_Center + X;
  1620.         SY1 := Y_Center - Y;
  1621.         SX2 := X_Center - X;
  1622.         SY2 := Y_Center + Y;
  1623.         Line(round(SX1), round(SY1), round(SX2), round(SY2))
  1624.       end {if}
  1625.       else begin
  1626.         SX1 := X_Center + X;
  1627.         SY1 := Y_Center - Y;
  1628.         SX2 := X_Center + X/2;
  1629.         SY2 := Y_Center - Y/2;
  1630.         Line(round(SX1), round(SY1), round(SX2), round(SY2));
  1631.         SX1 := X_Center - X;
  1632.         SY1 := Y_Center + Y;
  1633.         SX2 := X_Center - X/2;
  1634.         SY2 := Y_Center + Y/2;
  1635.         Line(round(SX1), round(SY1), round(SX2), round(SY2))
  1636.       end {else}
  1637.     end; {for}
  1638.  
  1639.     { Draw X-axis labels. }
  1640.  
  1641.     Draw_Mode(2);
  1642.     SY1 := Y_Center + 0.3*SF*Pix_per_Unit;
  1643.     for R := 1 to 4 do begin
  1644.       Num_to_Str(R*X_Scale, NumStr);
  1645.       SX1 := X_Center + R*Pix_per_Unit-8*Length(NumStr)/2;
  1646.       Draw_String(round(SX1), round(SY1), NumStr)
  1647.     end; {for}
  1648.  
  1649.     { Draw the angle labels. }
  1650.  
  1651.     for A := 1 to 23 do begin
  1652.       Rad := A*Pi/12;
  1653.       if (A<6) or (A>18) then
  1654.         SX1 := X_Center + 4.1*Cos(Rad)*Pix_per_Unit
  1655.       else
  1656.         SX1 := X_Center + 4.1*Cos(Rad)*Pix_per_Unit - 24;
  1657.       Temp := Y_Pix DIV 200;
  1658.       SY1 := Y_Center - 4*Sin(Rad)*(SF*Pix_per_Unit + Temp) + 4*Temp;
  1659.       Num_to_Str(15*A, NumStr);
  1660.       Draw_String(round(SX1), round(SY1), NumStr)
  1661.     end; {for}
  1662.     Draw_Mode(1)
  1663.   end; {Draw_PGrid}
  1664.  
  1665. {-----  Polar_Graph begins here  -----}
  1666.  
  1667. begin
  1668.   if Draw = ReDraw then
  1669.     Polar_Grid(X_Scale)
  1670.   else if Draw = Old_Grid then
  1671.     Restore_Screen(Display_Area, Temp_Screen)
  1672.   else
  1673.     Clear_Screen;
  1674.   Line_Color(Graph_Color);
  1675.   XPix_per_Unit := (X_Center/5)/X_Scale;
  1676.   YPix_per_Unit := SF*XPix_per_Unit;
  1677.   Line_to_Flag := FALSE;
  1678.   A := 0;
  1679.  
  1680.   { Since polar graphs don't have a fixed 'stopping' place, the program
  1681.     will continue plotting until the user presses a key.  The screen
  1682.     display will remain until the user presses another key. }
  1683.  
  1684.   repeat
  1685.     Angle := A*Pi/180;
  1686.     Radius := Evaluate(The_Function, Angle, Dummy_flag, Undefined);
  1687.     if NOT Undefined then begin
  1688.       SX := X_Center + Radius*Cos(Angle)*XPix_per_Unit;
  1689.       SY := Y_Center - Radius*Sin(Angle)*YPix_Per_Unit;
  1690.       if Abs(SY) < 32000 then
  1691.         if Line_to_Flag then
  1692.           Line_to(round(SX), round(SY))
  1693.         else begin
  1694.           Plot(round(SX), round(SY));
  1695.           Line_to_Flag := TRUE
  1696.         end {else}
  1697.       else
  1698.         Line_to_Flag := FALSE
  1699.     end {if}
  1700.     else
  1701.       Line_to_Flag := FALSE;
  1702.     A := A + 1;
  1703.     if A > 32000 then begin
  1704.       A := 0;
  1705.       Line_to_Flag := FALSE
  1706.     end {if}
  1707.   until keypress;
  1708.   read(Dummy)
  1709. end; {Draw_PGraph}
  1710.  
  1711.  
  1712. {***************  Save Screen  ******************
  1713. *                                               *
  1714. *  Save screen display.                         *
  1715. *                                               *
  1716. *  Called by: MAIN DRIVER                       *
  1717. *                                               *
  1718. *  In parameter: Pointer to physical screen     *
  1719. *  Out parameter: Screen storage area           *
  1720. ************************************************}
  1721.  
  1722. procedure Save_Screen(Display {in}: Screen_Ptr;
  1723.                       var Temp_Screen {out}: Screen_Ptr);
  1724.  
  1725. var
  1726.   I: integer;   {Loop control}
  1727.  
  1728. begin
  1729.   {$P-}
  1730.   for I := 0 to 31999 do
  1731.     Temp_Screen^[I] := Display^[I]
  1732.   {$P+}
  1733. end; {Save_Screen}
  1734.  
  1735.  
  1736. {-----------------------------------------
  1737.           M A I N   D R I V E R
  1738. -----------------------------------------}
  1739.  
  1740. begin
  1741.   if Init_Gem >= 0 then begin
  1742.     Hide_Mouse;
  1743.     Initialization;
  1744.     Get_Expression(Infix, PostFix);
  1745.     while InFix <> 'Q' do begin
  1746.  
  1747.       { Get the parameter values desired by the user. }
  1748.  
  1749.       while Get_Graph_Parameters_OK(Grid,X_Scale,Y_Scale,Color,Draw) do begin
  1750.         if (Grid = Rectangular) or (Grid = Trigonometric) then
  1751.           Rect_Graph(PostFix,X_Scale,Y_Scale,Color,Draw)
  1752.         else
  1753.           Polar_Graph(PostFix,X_Scale,Color,Draw);
  1754.  
  1755.         { Freeze display until user presses a key. }
  1756.  
  1757.         repeat
  1758.           Event := Get_Event(E_Button|E_Keyboard,1,1,1,0,FALSE,0,0,0,0,
  1759.                              FALSE,0,0,0,0,DummyMsg,D,D,D,D,D,D);
  1760.         until (Event = E_Button) or (Event = E_Keyboard);
  1761.  
  1762.         { Save the screen display. }
  1763.  
  1764.         Save_Screen(Display_Area, Temp_Screen)
  1765.       end; {while}
  1766.  
  1767.       { Return postfix storage to the available memory heap. }
  1768.  
  1769.       while PostFix <> NIL do begin
  1770.         TempPtr := PostFix;
  1771.         PostFix := PostFix^.Link;
  1772.         dispose(TempPtr)
  1773.       end; {while}
  1774.  
  1775.       { Get ready to do it again. }
  1776.  
  1777.       Get_Expression(Infix, PostFix)
  1778.     end; {while}
  1779.     Show_Mouse;
  1780.     Exit_Gem
  1781.   end {if}
  1782. end.
  1783.